package model

import (
	"context"
	"database/sql"

	"go-zero-admin/pkg/constant"

	"github.com/Masterminds/squirrel"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlc"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

var _ SysCasbinRuleModel = (*customSysCasbinRuleModel)(nil)

type (
	// SysCasbinRuleModel is an interface to be customized, add more methods here,
	// and implement the added methods in customSysCasbinRuleModel.
	SysCasbinRuleModel interface {
		sysCasbinRuleModel
		Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
		RowBuilder() squirrel.SelectBuilder
		CountBuilder(field string) squirrel.SelectBuilder
		SumBuilder(field string) squirrel.SelectBuilder
		FindOneByQuery(ctx context.Context, rowBuilder squirrel.SelectBuilder) (*SysCasbinRule, error)
		FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder) (float64, error)
		FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder) (int64, error)
		FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*SysCasbinRule, error)
		FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*SysCasbinRule, error)
		InsertEx(ctx context.Context, session sqlx.Session, dataList []*SysCasbinRule) ([]int64, error)
		DeleteEx(ctx context.Context, session sqlx.Session, ids []int64, isDel bool, userId string) error
		UpdateEx(ctx context.Context, session sqlx.Session, data *SysCasbinRule) error
	}

	customSysCasbinRuleModel struct {
		*defaultSysCasbinRuleModel
	}
)

// NewSysCasbinRuleModel returns a model for the database table.
func NewSysCasbinRuleModel(conn sqlx.SqlConn, c cache.CacheConf) SysCasbinRuleModel {
	return &customSysCasbinRuleModel{
		defaultSysCasbinRuleModel: newSysCasbinRuleModel(conn, c),
	}
}

func (m *defaultSysCasbinRuleModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
	return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
		return fn(ctx, session)
	})
}

func (m *defaultSysCasbinRuleModel) RowBuilder() squirrel.SelectBuilder {
	return squirrel.Select(sysCasbinRuleRows).From(m.table)
}

func (m *defaultSysCasbinRuleModel) CountBuilder(field string) squirrel.SelectBuilder {
	return squirrel.Select("COUNT(" + field + ")").From(m.table)
}

func (m *defaultSysCasbinRuleModel) SumBuilder(field string) squirrel.SelectBuilder {
	return squirrel.Select("IFNULL(SUM(" + field + "), 0)").From(m.table)
}

func (m *defaultSysCasbinRuleModel) FindOneByQuery(ctx context.Context, rowBuilder squirrel.SelectBuilder) (*SysCasbinRule, error) {
	query, values, err := rowBuilder.Where(squirrel.Eq{"del_state": constant.DelStateNo}).ToSql()
	if err != nil {
		return nil, err
	}

	var resp SysCasbinRule
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultSysCasbinRuleModel) FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder) (float64, error) {
	query, values, err := sumBuilder.Where(squirrel.Eq{"del_state": constant.DelStateNo}).ToSql()
	if err != nil {
		return 0, err
	}

	var resp float64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

func (m *defaultSysCasbinRuleModel) FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder) (int64, error) {
	query, values, err := countBuilder.Where(squirrel.Eq{"del_state": constant.DelStateNo}).ToSql()
	if err != nil {
		return 0, err
	}

	var resp int64
	err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return 0, err
	}
}

func (m *defaultSysCasbinRuleModel) FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*SysCasbinRule, error) {
	if orderBy == "" {
		rowBuilder = rowBuilder.OrderBy(m.primaryField() + " DESC")
	} else {
		rowBuilder = rowBuilder.OrderBy(orderBy)
	}

	query, values, err := rowBuilder.Where(squirrel.Eq{"del_state": constant.DelStateNo}).ToSql()
	if err != nil {
		return nil, err
	}

	var resp []*SysCasbinRule
	err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return nil, err
	}
}

func (m *defaultSysCasbinRuleModel) FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*SysCasbinRule, error) {
	if orderBy == "" {
		rowBuilder = rowBuilder.OrderBy(m.primaryField() + " DESC")
	} else {
		rowBuilder = rowBuilder.OrderBy(orderBy)
	}

	if page < 1 {
		page = 1
	}
	if pageSize < 1 {
		pageSize = 10
	}
	offset := (page - 1) * pageSize

	query, values, err := rowBuilder.Where(squirrel.Eq{"del_state": constant.DelStateNo}).Offset(uint64(offset)).Limit(uint64(pageSize)).ToSql()
	if err != nil {
		return nil, err
	}

	var resp []*SysCasbinRule
	err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return nil, err
	}
}

func (m *defaultSysCasbinRuleModel) InsertEx(ctx context.Context, session sqlx.Session, dataList []*SysCasbinRule) ([]int64, error) {
	// 判断外部是否有事务，如果没事务，自己开启事务，方便数据库插入和搜索引擎插入的同步和回滚
	if session != nil {
		return m.insertLogic(ctx, session, dataList)
	}

	// 外部无事务
	var rtIds []int64
	if err := m.Trans(ctx, func(c context.Context, si sqlx.Session) error {
		inIds, err := m.insertLogic(ctx, si, dataList)
		if err != nil {
			return err
		}
		rtIds = inIds
		return nil
	}); err != nil {
		return []int64{}, err
	}
	return rtIds, nil
}

func (m *defaultSysCasbinRuleModel) DeleteEx(ctx context.Context, session sqlx.Session, ids []int64, isDel bool, userId string) error {
	// 判断外部是否有事务，如果没事务，自己开启事务，方便数据库删除和搜索引擎删除的同步和回滚
	if session != nil {
		return m.deleteLogic(ctx, session, ids, isDel, userId)
	}

	// 外部无事务
	if err := m.Trans(ctx, func(c context.Context, si sqlx.Session) error {
		return m.deleteLogic(ctx, si, ids, isDel, userId)
	}); err != nil {
		return err
	}
	return nil
}

func (m *defaultSysCasbinRuleModel) UpdateEx(ctx context.Context, session sqlx.Session, data *SysCasbinRule) error {
	// 判断外部是否有事务，如果没事务，自己开启事务，方便数据库更新和搜索引擎更新的同步和回滚
	if session != nil {
		return m.updateLogic(ctx, session, data)
	}

	// 外部无事务
	if err := m.Trans(ctx, func(c context.Context, si sqlx.Session) error {
		return m.updateLogic(ctx, si, data)
	}); err != nil {
		return err
	}
	return nil
}

// 新增逻辑
func (m *defaultSysCasbinRuleModel) insertLogic(ctx context.Context, session sqlx.Session, dataList []*SysCasbinRule) ([]int64, error) {
	var err error
	var resp sql.Result
	var rtIds []int64
	if len(dataList) == 1 {
		resp, err = m.insert(ctx, session, dataList[0])
		if err == nil {
			rtId, _ := resp.LastInsertId()
			rtIds = append(rtIds, rtId)
		}
	} else {
		resp, err = m.inserts(ctx, session, dataList)
		if err == nil {
			rtId, _ := resp.LastInsertId()
			rtIds = append(rtIds, rtId)
			for i := 1; i < len(dataList); i++ {
				rtIds = append(rtIds, rtId+int64(i))
			}
		}
	}
	if err != nil {
		return []int64{}, err
	}

	//TODO 扩展搜索引擎

	return rtIds, nil
}

// 删除逻辑
func (m *defaultSysCasbinRuleModel) deleteLogic(ctx context.Context, session sqlx.Session, ids []int64, isDel bool, userId string) error {
	var err error
	if len(ids) == 1 {
		if isDel {
			err = m.delete(ctx, session, ids[0])
		} else {
			err = m.deleteSoft(ctx, session, ids[0], userId)
		}
	} else {
		if isDel {
			err = m.deletes(ctx, session, ids)
		} else {
			err = m.deletesSoft(ctx, session, ids, userId)
		}
	}
	if err != nil {
		return err
	}

	//TODO 扩展搜索引擎

	return nil
}

// 更新逻辑
func (m *defaultSysCasbinRuleModel) updateLogic(ctx context.Context, session sqlx.Session, data *SysCasbinRule) error {
	var err error
	err = m.update(ctx, session, data)
	if err != nil {
		return err
	}

	//TODO 扩展搜索引擎

	return nil
}
